/*
 * Copyright (c) 2007 The University of Reading
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University of Reading, nor the names of the
 *    authors or contributors may be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package uk.ac.rdg.resc.wms2kml;

import java.util.ArrayList;
import java.util.List;
import org.simpleframework.xml.Attribute;
import org.simpleframework.xml.Element;
import org.simpleframework.xml.ElementList;
import org.simpleframework.xml.Root;
import org.simpleframework.xml.Text;
import org.simpleframework.xml.load.PersistenceException;
import org.simpleframework.xml.load.Validate;

/**
 * A Layer in a WMS 1.1.1 capabilities document
 * 
 * @author Jon Blower
 * $Revision$
 * $Date$
 * $Log$
 * @todo Make sure this inherits the necessary properties from its parents
 * @todo dimensions
 */
@Root(name="Layer", strict=false)
public class Layer1_1_1 extends Layer
{
    @Element(name="Name", required=false)
    private String name = null; // If a Layer has a name it is displayable
    
    @Element(name="Title")
    private String title;
    
    @Element(name="Abstract", required=false)
    private String abstr = null;
    
    // This is required if this is a displayable Layer.
    // We check for this in the validate() method.
    @Element(name="LatLonBoundingBox", required=false)
    private LatLonBoundingBox bbox = null;
    
    @ElementList(inline=true, required=false)
    private List<Layer1_1_1> children = new ArrayList<Layer1_1_1>();
    
    @ElementList(inline=true, required=false)
    private List<Dimension1_1_1> dimensions = new ArrayList<Dimension1_1_1>();
    
    @ElementList(inline=true, required=false)
    private List<Extent> extents = new ArrayList<Extent>();
    
    @ElementList(inline=true, required=false)
    private List<SRS> srss = new ArrayList<SRS>();
    
    // We set these properties in the validate() method
    private List<String> elevationValues = null;
    private String elevationUnits = null;
    private List<String> isoTimeValues = null;

    public String getName()
    {
        return name;
    }

    public String getTitle()
    {
        return title;
    }

    public String getDescription()
    {
        return this.abstr;
    }

    /**
     * @return the children Layers, or an empty list if this Layer1_3 has no children
     */
    public List<Layer1_1_1> getChildren()
    {
        return children;
    }
    
    public BoundingBox getBbox()
    {
        return bbox;
    }
    
    @Validate
    public void validate() throws PersistenceException
    {
        // Check that a displayable layer also has a bounding box
        if (this.name != null && this.bbox == null)
        {
            throw new PersistenceException("A displayable Layer must have a bounding box");
        }
        // Check that the Dimensions and Extents are valid
        for (Dimension1_1_1 dim : this.dimensions)
        {
            if (dim.name.equalsIgnoreCase("elevation"))
            {
                // Need to find an Extent with the same name
                boolean found = false;
                for (Extent extent : this.extents)
                {
                    if (extent.name.equalsIgnoreCase("elevation"))
                    {
                        found = true;
                        this.elevationValues = new ArrayList<String>();
                        for (String elVal : extent.values.split(","))
                        {
                            this.elevationValues.add(elVal.trim());
                        }
                        this.elevationUnits = dim.units;
                    }
                }
                if (!found)
                {
                    throw new PersistenceException("No matching Extent found for the elevation dimension");
                }
            }
            else if (dim.name.equalsIgnoreCase("time"))
            {
                if (dim.units.equalsIgnoreCase("ISO8601") ||
                    dim.units.equalsIgnoreCase("ISO 8601")) // TODO is the latter allowed?
                {
                    // Need to find an Extent with the same name
                    boolean found = false;
                    for (Extent extent : this.extents)
                    {
                        if (extent.name.equalsIgnoreCase("time"))
                        {
                            found = true;
                            this.isoTimeValues = parseTimeValues(extent.values);
                        }
                    }
                    if (!found)
                    {
                        // Some WMS servers (e.g. http://onearth.jpl.nasa.gov/wms.cgi) are
                        // naughty and specify values in the Dimension tag instead of using
                        // a corresponding Extent tag
                        if (dim.values == null)
                        {
                            throw new PersistenceException("No values or matching Extent found for the time dimension");
                        }
                        this.isoTimeValues = parseTimeValues(dim.values);
                    }
                }
                else
                {
                    throw new PersistenceException("Invalid units for the TIME dimension: "
                        + dim.units);
                }
            }
        }
        // Set the supported projections for this layer
        for (SRS srs : this.srss)
        {
            this.supportedProjections.add(srs.srsCode);
        }
    }
    
    /**
     * @return a list of possible values of elevation that this Layer can
     * take.  If this layer does not have an elevation dimension this will
     * return null.
     * @todo make sure this gets called only once, perhaps in Commit().
     */
    public List<String> getElevationValues()
    {
        return this.elevationValues;
    }
    
    public String getElevationUnits()
    {
        return this.elevationUnits;
    }

    public List<String> getTimeValues()
    {
        return this.isoTimeValues;
    }

    protected String getPlateCarreeProjectionCode()
    {
        return WMS1_1_1Capabilities.PLATE_CARREE_PROJ_CODE;
    }
}

@Root(name="SRS")
class SRS
{
    @Text
    String srsCode;
}

@Root(name="Dimension", strict=false)
class Dimension1_1_1
{
    @Attribute(name="name")
    String name;
    
    @Attribute(name="units")
    String units;
    
    // Some WMS servers (e.g. http://onearth.jpl.nasa.gov/wms.cgi) are
    // naughty and specify values in the Dimension tag instead of a corresponding
    // Extent tag
    @Text(required=false)
    String values = null;
}

@Root(name="Extent", strict=false)
class Extent
{
    @Attribute(name="name")
    String name;
    
    @Text
    String values;
}
